package com.xw.unification.interceptor;

import com.xw.unification.entity.request.CopyOnReadHttpServletRequest;
import com.xw.unification.exception.RequestBodyOverLengthException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.PriorityOrdered;

import com.xw.unification.config.WebUnificationMvcProperties.RepeatableRequestProperties;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;

@WebFilter
public class RepeatableRequestFilter implements Filter, PriorityOrdered {

    public static final Logger LOGGER = LoggerFactory.getLogger(RepeatableRequestFilter.class);

    private FilterConfig filterConfig;
    private RepeatableRequestProperties properties;

    public RepeatableRequestFilter(RepeatableRequestProperties properties) {
        this.properties = properties;
        String cacheFolderPath = properties.getRequestBodyCacheFolderPath();
    }

    @Override
    public int getOrder() {
        return PriorityOrdered.HIGHEST_PRECEDENCE;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request == null) {
            throw new IllegalArgumentException("ServletRequest can not be null");
        }
        if (request instanceof HttpServletRequest) {
            String contentType = request.getContentType();
            if (contentType == null && properties.isAllowNoContentType()) {
                request = getRepeatableHttpServletRequest((HttpServletRequest) request);
            } else if (contentType != null) {
                List<String> contentTypes = properties.getRepeatableContentTypes();
                if (!contentType.isEmpty()) {
                    for (String type : contentTypes) {
                        if (type == null) {
                            continue;
                        }
                        type = type.toLowerCase();
                        if (contentType.toLowerCase().startsWith(type)) {
                            request = getRepeatableHttpServletRequest((HttpServletRequest) request);
                            break;
                        }
                    }
                }
            }
        }
        chain.doFilter(request, response);
    }

    private HttpServletRequest getRepeatableHttpServletRequest(HttpServletRequest request) {
        long lengthLong = request.getContentLengthLong();
        long maxLength = properties.getMaxContentLength();
        if (lengthLong > maxLength) {
            LOGGER.warn("Content length : {} is more than max value: {}, skip request overwriting", lengthLong, maxLength);
            return request;
        }
        boolean overInt = lengthLong > Integer.MAX_VALUE;
        if (overInt) {
            if (properties.isSkipIfContentLengthOverInt()) {
                LOGGER.info("Content length: {} > integer max value: {}, skip request overwriting", lengthLong, Integer.MAX_VALUE);
                return request;
            } else {
                LOGGER.warn("Content length: {} > integer max value: {}, force to overwrite request", lengthLong, Integer.MAX_VALUE);
            }
        }
        try {
            if (!overInt) {
                return new CopyOnReadHttpServletRequest(request);
            } else {
                // TODO: request body length > Integer.MAX_VALUE, 将 body 缓存为文件处理
                return request;
            }
        } catch (RequestBodyOverLengthException e) {
            if (properties.isSkipIfContentLengthOverInt()) {
                LOGGER.warn("Content length is more than integer max value: {}, skip request overwriting", Integer.MAX_VALUE);
                return request;
            } else {
                throw e;
            }
        }
    }

}
